#define DIRECTINPUT_VERSION 0x0700

#include <Windows.h>
#include <dinput.h>
#include <stdio.h>
#include <vector>

#include "resource.h"

#pragma comment(lib, "libs/dinput.lib")
#pragma comment(lib, "libs/dxguid.lib")

HINSTANCE hInst;
HWND hWnd;
std::vector<DIDEVICEOBJECTINSTANCE> objects;

BOOL CALLBACK func(LPDIDEVICEOBJECTINSTANCE lpdidoi, LPVOID pvRef)
{
	for(unsigned int i = 0; i < objects.size(); i++)
	{
		if(memcmp(&(objects[i]), lpdidoi, sizeof(objects[i])) == 0) return DIENUM_STOP;
	}

//	LPDIDEVICEOBJECTINSTANCE tmp = new DIDEVICEOBJECTINSTANCE;
//	memcpy(tmp, lpdidoi, lpdidoi->dwSize);
	objects.push_back(*lpdidoi);

	return DIENUM_CONTINUE;
}

BOOL GatherMouseData()
{
	LPDIRECTINPUT lpDI = NULL;
	LPDIRECTINPUTDEVICE lpDIDMouse = NULL;

	HRESULT rv = DirectInputCreate(hInst, DIRECTINPUT_VERSION, &lpDI, NULL);
	if(rv != DI_OK)
	{
		MessageBox(hWnd, "DirectInput failed it's init process!", "Error!", MB_OK | MB_ICONERROR);
		return FALSE;
	}

	rv = lpDI->CreateDevice(GUID_SysMouse, &lpDIDMouse, NULL);
	if(rv != DI_OK)
	{
		MessageBox(hWnd, "DirectInput failed to create a mouse device!", "Error!", MB_OK | MB_ICONERROR);
		lpDI->Release();
		return FALSE;
	}

	rv = lpDIDMouse->SetCooperativeLevel(hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
	if(rv != DI_OK)
	{
		MessageBox(hWnd, "DirectInput failed to set cooperative level with the mouse!", "Error!", MB_OK | MB_ICONERROR);
		lpDIDMouse->Release();
		lpDI->Release();
		return FALSE;
	}

	rv = lpDIDMouse->SetDataFormat(&c_dfDIMouse);
	if(rv != DI_OK)
	{
		MessageBox(hWnd, "DirectInput failed to set the mouse data format!", "Error!", MB_OK | MB_ICONERROR);	
		lpDIDMouse->Release();
		lpDI->Release();
		return FALSE;
	}

	rv = lpDIDMouse->Acquire();
	if(rv != DI_OK)
	{
		MessageBox(hWnd, "DirectInput failed to acquire the mouse!", "Error!", MB_OK | MB_ICONERROR);
		lpDIDMouse->Release();
		lpDI->Release();
		return FALSE;
	}

	rv = lpDIDMouse->EnumObjects((LPDIENUMDEVICEOBJECTSCALLBACK)func, NULL, DIDFT_ALL);
	if(rv != DI_OK)
	{
		MessageBox(hWnd, "Gathering the data failed!", "Error!", MB_OK | MB_ICONERROR);
		lpDIDMouse->Unacquire();
		lpDIDMouse->Release();
		lpDI->Release();
		return FALSE;
	}

	lpDIDMouse->Unacquire();
	lpDIDMouse->Release();
	lpDI->Release();
	return TRUE;
}

BOOL PrintData()
{
	FILE* fp = fopen("result.log", "w");
	if(fp == NULL) return FALSE;

	for(unsigned int i = 0; i < objects.size(); i++)
	{
		if(fprintf(fp, "--OBJECT %u--\n", i) < 0) break;

		if(fprintf(fp, "Size: %u\n", objects[i].dwSize) < 0) break;

		char type[16];
		if(objects[i].guidType == GUID_XAxis)			strcpy(type, "GUID_XAxis");
		else if(objects[i].guidType == GUID_YAxis)		strcpy(type, "GUID_YAxis");
		else if(objects[i].guidType == GUID_ZAxis)		strcpy(type, "GUID_ZAxis");
		else if(objects[i].guidType == GUID_RxAxis)		strcpy(type, "GUID_RxAxis");
		else if(objects[i].guidType == GUID_RyAxis)		strcpy(type, "GUID_RyAxis");
		else if(objects[i].guidType == GUID_RzAxis)		strcpy(type, "GUID_RzAxis");
		else if(objects[i].guidType == GUID_Slider)		strcpy(type, "GUID_Slider");
		else if(objects[i].guidType == GUID_Button)		strcpy(type, "GUID_Button");
		else if(objects[i].guidType == GUID_Key)		strcpy(type, "GUID_Key");
		else if(objects[i].guidType == GUID_POV)		strcpy(type, "GUID_POV");
		else if(objects[i].guidType == GUID_Unknown)	strcpy(type, "GUID_Unknown");
		else strcpy(type, "Unknown Type?");
		if(fprintf(fp, "GUID Type: %s\n", type) < 0) break;

		if(fprintf(fp, "Offset: %X\n", objects[i].dwOfs) < 0) break;

		if(fprintf(fp, "Device type: %X\n", objects[i].dwType) < 0) break;

		char flags[1024] = { '\0' };
		if((objects[i].dwFlags & DIDOI_ASPECTACCEL) == DIDOI_ASPECTACCEL)			strcat(flags, "DIDOI_ASPECTACCEL, ");
		if((objects[i].dwFlags & DIDOI_ASPECTFORCE) == DIDOI_ASPECTFORCE)			strcat(flags, "DIDOI_ASPECTFORCE, ");
		if((objects[i].dwFlags & DIDOI_ASPECTMASK) == DIDOI_ASPECTMASK)				strcat(flags, "DIDOI_ASPECTMASK, ");
		if((objects[i].dwFlags & DIDOI_ASPECTPOSITION) == DIDOI_ASPECTPOSITION)		strcat(flags, "DIDOI_ASPECTPOSITION, ");
		if((objects[i].dwFlags & DIDOI_ASPECTVELOCITY) == DIDOI_ASPECTVELOCITY) 	strcat(flags, "DIDOI_ASPECTVELOCITY, ");
		if((objects[i].dwFlags & DIDOI_FFACTUATOR) == DIDOI_FFACTUATOR)				strcat(flags, "DIDOI_FFACTUATOR, ");
		if((objects[i].dwFlags & DIDOI_FFEFFECTTRIGGER) == DIDOI_FFEFFECTTRIGGER)	strcat(flags, "DIDOI_FFEFFECTTRIGGER, ");
		if((objects[i].dwFlags & DIDOI_GUIDISUSAGE) == DIDOI_GUIDISUSAGE)			strcat(flags, "DIDOI_GUIDISUSAGE, ");
		if((objects[i].dwFlags & DIDOI_POLLED) == DIDOI_POLLED)						strcat(flags, "DIDOI_POLLED, ");
		if(flags[0] != '\0') flags[strlen(flags) - 2] = '\0';
		if(fprintf(fp, "Flags: %s\n", flags) < 0) break;

		if(fprintf(fp, "Object name: %s\n", objects[i].tszName) < 0) break;

		if(fprintf(fp, "ForceFeedback Max Force: %u\n", objects[i].dwFFMaxForce) < 0) break;

		if(fprintf(fp, "ForceFeedback Force Resolution: %u\n", objects[i].dwFFForceResolution) < 0) break;

		if(fprintf(fp, "HID Collection Number: %u\n", objects[i].wCollectionNumber) < 0) break;

		if(fprintf(fp, "HID Designator Index: %u\n", objects[i].wDesignatorIndex) < 0) break;

		if(fprintf(fp, "HID Usage Page: %u\n", objects[i].wUsagePage) < 0) break;

		if(fprintf(fp, "HID Usage: %u\n", objects[i].wUsage) < 0) break;

		if(fprintf(fp, "HID Dimensional Units Code: %X\n", objects[i].dwDimension) < 0) break;

		if(fprintf(fp, "HID Dimensional Exponent: %d\n", objects[i].wExponent) < 0) break;

		if(fprintf(fp, "Report ID: %X\n", objects[i].wReportId) < 0) break;

		if(fprintf(fp, "\n\n") < 0) break;
	}

	if(ferror(fp))
	{
		fclose(fp);
		return FALSE;
	}

	fclose(fp);

	return TRUE;
}

LRESULT CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
		case WM_DESTROY:
			hWnd = NULL;
		case WM_QUIT:
		case WM_CLOSE:
			objects.clear();
			PostQuitMessage(0);
			break;
		case WM_COMMAND:
		{
			DWORD command = LOWORD(wParam);
			switch(command)
			{
				case ID_BUTTON_RUN:
				{
					BOOL res = GatherMouseData();
					if(res == FALSE)
					{
						return FALSE;
					}
					res = PrintData();
					if(res == FALSE)
					{
						MessageBox(hWnd, "Printing the gathered data failed!", "Error!", MB_OK | MB_ICONERROR);
						return FALSE;
					}
					MessageBox(hWnd, "Gathering the data was successful!\nIt's now safe to Quit the program", "Success!", MB_OK);
					break;
				}
				case ID_BUTTON_QUIT:
				{
					PostMessage(hDlg, WM_CLOSE, 0, 0);
					break;
				}
			}
		}
		default: return FALSE;
	}
	return TRUE;
}

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   //HWND hWnd;

   hInst = hInstance; // Store instance handle in our global variable

   hWnd = CreateDialog(hInst, MAKEINTRESOURCE(IDD_DIALOG1), NULL, (DLGPROC)DlgProc);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
	MSG msg;

	// Perform application initialization:
	if (!InitInstance (hInstance, nCmdShow))
	{
		return FALSE;
	}

	// Main message loop:
	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);

		if(msg.message == WM_QUIT) break;
	}

	return (int) msg.wParam;
}
